// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stddef.h>
#include <stdint.h>

#include <random>

#include "base/at_exit.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/run_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media.h"
#include "media/base/media_util.h"
#include "media/filters/vpx_video_decoder.h"

using namespace media;

struct Env {
    Env()
    {
        InitializeMediaLibrary();
        base::CommandLine::Init(0, nullptr);
        logging::SetMinLogLevel(logging::LOG_FATAL);
    }

    base::AtExitManager at_exit_manager;
    base::MessageLoop message_loop;
};
Env* env = new Env();

void OnDecodeComplete(const base::Closure& quit_closure, DecodeStatus status)
{
    quit_closure.Run();
}

void OnInitDone(const base::Closure& quit_closure,
    bool* success_dest,
    bool success)
{
    *success_dest = success;
    quit_closure.Run();
}

void OnOutputComplete(const scoped_refptr<VideoFrame>& frame) { }

// Entry point for LibFuzzer.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
    std::mt19937_64 rng;

    { // Seed rng from data.
        std::string str = std::string(reinterpret_cast<const char*>(data), size);
        std::size_t data_hash = std::hash<std::string>()(str);
        rng.seed(data_hash);
    }

    // Compute randomized constants. Put all rng() usages here.
    // Use only values that pass DCHECK in VpxVideoDecoder::ConfigureDecoder().
    VideoCodec codec;
    VideoPixelFormat pixel_format;
    if (rng() & 1) {
        codec = kCodecVP8;
        // PIXEL_FORMAT_YV12 disabled for kCodecVP8 on Linux.
        pixel_format = PIXEL_FORMAT_YV12A;
    } else {
        codec = kCodecVP9;
        switch (rng() % 3) {
        case 0:
            pixel_format = PIXEL_FORMAT_YV12;
            break;
        case 1:
            pixel_format = PIXEL_FORMAT_YV12A;
            break;
        case 2:
            pixel_format = PIXEL_FORMAT_YV24;
            break;
        default:
            return 0;
        }
    }

    auto profile = static_cast<VideoCodecProfile>(rng() % VIDEO_CODEC_PROFILE_MAX);
    auto color_space = static_cast<ColorSpace>(rng() % COLOR_SPACE_MAX);
    auto coded_size = gfx::Size(1 + (rng() % 127), 1 + (rng() % 127));
    auto visible_rect = gfx::Rect(coded_size);
    auto natural_size = gfx::Size(1 + (rng() % 127), 1 + (rng() % 127));

    VideoDecoderConfig config(codec, profile, pixel_format, color_space,
        coded_size, visible_rect, natural_size,
        EmptyExtraData(), Unencrypted());

    if (!config.IsValidConfig())
        return 0;

    VpxVideoDecoder decoder;

    {
        base::RunLoop run_loop;
        bool success = false;
        decoder.Initialize(
            config, true /* low_delay */, nullptr /* cdm_context */,
            base::Bind(&OnInitDone, run_loop.QuitClosure(), &success),
            base::Bind(&OnOutputComplete));
        run_loop.Run();
        if (!success)
            return 0;
    }

    {
        base::RunLoop run_loop;
        auto buffer = DecoderBuffer::CopyFrom(data, size);
        decoder.Decode(buffer,
            base::Bind(&OnDecodeComplete, run_loop.QuitClosure()));
        run_loop.Run();
    }

    return 0;
}
